package wiki.xsx.core.jmeter.core.sampler;

import wiki.xsx.core.jmeter.core.assertion.JmeterAssertion;
import wiki.xsx.core.jmeter.core.enums.JmeterHttpProtocol;
import wiki.xsx.core.jmeter.core.postprocessor.JmeterPostProcessor;
import wiki.xsx.core.jmeter.core.preprocessor.JmeterPreProcessor;
import wiki.xsx.core.jmeter.core.util.JmeterOptional;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.http.util.HTTPConstants;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;

import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 默认http采样器配置
 *
 * @author xsx
 * @date 2022/8/22
 * @since 1.8
 * <p>
 * Copyright (c) 2022 xsx All Rights Reserved.
 * easy-jmeter is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * </p>
 */
@Data
@Accessors(chain = true)
public class JmeterDefaultHttpSamplerConfig implements JmeterHttpSamplerConfig {

    /**
     * 请求头
     */
    private Map<String, String> requestHeaders;
    /**
     * 样本名称
     */
    private String name;
    /**
     * 样本注释
     */
    private String comment;
    /**
     * 请求地址
     */
    private String domain;
    /**
     * 请求端口
     */
    private Integer port;
    /**
     * 请求路径
     */
    private String path;
    /**
     * 请求类型
     */
    private String method;
    /**
     * 请求协议
     */
    private String protocol;
    /**
     * 是否请求体参数
     */
    private Boolean isRequestBodyParameter;
    /**
     * 请求参数
     */
    private Map<String, String> parameters;
    /**
     * 内容编码
     */
    private String contentEncoding;
    /**
     * 连接超时时间（单位：毫秒）
     */
    private Integer connectTimeout;
    /**
     * 响应超时时间（单位：毫秒）
     */
    private Integer responseTimeout;
    /**
     * 是否长连接
     */
    private Boolean isUseKeepAlive;
    /**
     * 是否自动重定向
     */
    private Boolean isAutoRedirects;
    /**
     * 是否跟随重定向
     */
    private Boolean isFollowRedirects;
    /**
     * 是否浏览器兼容
     */
    private Boolean isBrowserCompatible;

    /**
     * 断言列表
     */
    private List<JmeterAssertion> assertions;
    /**
     * 前置处理器列表
     */
    private List<JmeterPreProcessor> preProcessors;
    /**
     * 后置处理器列表
     */
    private List<JmeterPostProcessor> postProcessors;

    /**
     * 无参构造
     */
    private JmeterDefaultHttpSamplerConfig() {
    }

    /**
     * 获取配置实例
     *
     * @return 返回配置实例
     */
    public static JmeterDefaultHttpSamplerConfig getInstance() {
        return new JmeterDefaultHttpSamplerConfig();
    }

    /**
     * 初始化断言容量
     *
     * @param initialCapacity 初始容量
     * @return 返回http采样器配置
     */
    public JmeterDefaultHttpSamplerConfig initialAssertionCapacity(int initialCapacity) {
        this.assertions = new ArrayList<>(initialCapacity);
        return this;
    }

    /**
     * 初始化前置处理器容量
     *
     * @param initialCapacity 初始容量
     * @return 返回http采样器配置
     */
    public JmeterDefaultHttpSamplerConfig initialPreProcessorCapacity(int initialCapacity) {
        this.preProcessors = new ArrayList<>(initialCapacity);
        return this;
    }

    /**
     * 初始化后置处理器容量
     *
     * @param initialCapacity 初始容量
     * @return 返回http采样器配置
     */
    public JmeterDefaultHttpSamplerConfig initialPostProcessorCapacity(int initialCapacity) {
        this.postProcessors = new ArrayList<>(initialCapacity);
        return this;
    }

    /**
     * 添加断言
     *
     * @param assertions 断言
     * @return 返回http采样器配置
     */
    public JmeterDefaultHttpSamplerConfig addAssertion(JmeterAssertion... assertions) {
        // 如果断言列表为空，则初始化断言列表
        if (this.assertions == null) {
            // 初始化断言列表
            this.assertions = new ArrayList<>(10);
        }
        // 添加断言
        Collections.addAll(this.assertions, assertions);
        // 返回http采样器配置
        return this;
    }

    /**
     * 添加前置处理器
     *
     * @param preProcessors 前置处理器
     * @return 返回http采样器配置
     */
    public JmeterDefaultHttpSamplerConfig addPreProcessor(JmeterPreProcessor... preProcessors) {
        // 如果前置处理器列表为空，则初始化前置处理器列表
        if (this.preProcessors == null) {
            // 初始化前置处理器列表
            this.preProcessors = new ArrayList<>(10);
        }
        // 添加前置处理器
        Collections.addAll(this.preProcessors, preProcessors);
        // 返回http采样器配置
        return this;
    }

    /**
     * 添加后置处理器
     *
     * @param postProcessors 后置处理器
     * @return 返回http采样器配置
     */
    public JmeterDefaultHttpSamplerConfig addPostProcessor(JmeterPostProcessor... postProcessors) {
        // 如果后置处理器列表为空，则初始化后置处理器列表
        if (this.postProcessors == null) {
            // 初始化后置处理器列表
            this.postProcessors = new ArrayList<>(10);
        }
        // 添加后置处理器
        Collections.addAll(this.postProcessors, postProcessors);
        // 返回http采样器配置
        return this;
    }

    /**
     * 创建http采样器
     *
     * @return 返回http采样器配置树
     */
    @Override
    public HashTree create() {
        // 创建线程组配置树
        HashTree httpSamplerTree = new ListedHashTree();
        // 创建http采样器
        HTTPSamplerProxy httpSamplerProxy = new HTTPSamplerProxy();
        // 设置测试类名称
        httpSamplerProxy.setProperty(TestElement.TEST_CLASS, PROXY_TEST_CLASS_NAME);
        // 设置GUI类名称
        httpSamplerProxy.setProperty(TestElement.GUI_CLASS, PROXY_GUI_CLASS_NAME);
        // 设置启用
        httpSamplerProxy.setEnabled(true);
        // 设置样本名称
        httpSamplerProxy.setName(JmeterOptional.ofNullable(this.path).orElse(this.domain));
        // 设置样本注释
        httpSamplerProxy.setComment(JmeterOptional.ofNullable(this.comment).orElse(""));
        // 设置请求地址
        httpSamplerProxy.setDomain(JmeterOptional.ofNullable(this.domain).orElseThrow(() -> new IllegalArgumentException("the domain can not be null")));
        // 如果端口不为空，则设置端口
        JmeterOptional.ofNullable(this.port).ifPresent(httpSamplerProxy::setPort);
        // 如果请求路径不为空，则设置请求路径
        JmeterOptional.ofNullable(this.path).ifPresent(httpSamplerProxy::setPath);
        // 设置请求类型（默认：GET）
        httpSamplerProxy.setMethod(JmeterOptional.ofNullable(this.method).orElse(HTTPConstants.GET).toUpperCase());
        // 设置内容编码（默认：utf-8）
        httpSamplerProxy.setContentEncoding(JmeterOptional.ofNullable(this.contentEncoding).orElse(StandardCharsets.UTF_8.name()));
        // 如果为请求体参数，则设置为请求体参数
        JmeterOptional.ofNullable(this.isRequestBodyParameter).ifTrue(httpSamplerProxy::setPostBodyRaw);
        // 设置请求协议（默认：http）
        httpSamplerProxy.setProtocol(JmeterOptional.ofNullable(this.protocol).orElse(JmeterHttpProtocol.HTTP.name()));
        // 如果连接超时时间不为空，则设置连接超时时间
        JmeterOptional.ofNullable(this.connectTimeout).ifPresent(v -> httpSamplerProxy.setConnectTimeout(v.toString()));
        // 如果响应超时时间不为空，则设置响应超时时间
        JmeterOptional.ofNullable(this.responseTimeout).ifPresent(v -> httpSamplerProxy.setResponseTimeout(v.toString()));
        // 如果是否长连接不为空，则设置是否长连接
        JmeterOptional.ofNullable(this.isUseKeepAlive).ifPresent(httpSamplerProxy::setUseKeepAlive);
        // 如果是否自动重定向不为空，则设置是否自动重定向
        JmeterOptional.ofNullable(this.isAutoRedirects).ifPresent(httpSamplerProxy::setAutoRedirects);
        // 如果是否跟随重定向不为空，则设置是否跟随重定向
        JmeterOptional.ofNullable(this.isFollowRedirects).ifPresent(httpSamplerProxy::setFollowRedirects);
        // 如果是否浏览器兼容不为空，则设置是否浏览器兼容
        JmeterOptional.ofNullable(this.isBrowserCompatible).ifPresent(httpSamplerProxy::setDoBrowserCompatibleMultipart);
        // 设置请求头管理器
        httpSamplerProxy.setHeaderManager(this.initHeaderManager());
        // 设置请求参数
        httpSamplerProxy.setArguments(this.initArguments());
        // 添加http采样器
        HashTree childTree = httpSamplerTree.add(httpSamplerProxy);
        // 如果http请求头管理器不为空，则添加请求头管理器
        JmeterOptional.ofNullable(httpSamplerProxy.getHeaderManager()).ifPresent(childTree::add);
        // 如果断言不为空，则添加断言
        JmeterOptional.ofNullable(this.assertions).ifPresent(
                v -> v.forEach(assertion -> childTree.add(assertion.create()))
        );
        // 如果前置处理器不为空，则添加前置处理器
        JmeterOptional.ofNullable(this.preProcessors).ifPresent(
                v -> v.forEach(preProcessor -> childTree.add(preProcessor.create()))
        );
        // 如果后置处理器不为空，则添加后置处理器
        JmeterOptional.ofNullable(this.postProcessors).ifPresent(
                v -> v.forEach(postProcessor -> childTree.add(postProcessor.create()))
        );
        // 返回http采样器
        return httpSamplerTree;
    }

    /**
     * 初始化请求头管理器
     *
     * @return 返回请求头管理器
     */
    private HeaderManager initHeaderManager() {
        // 创建请求头管理器
        HeaderManager headerManager = new HeaderManager();
        // 设置测试类名称
        headerManager.setProperty(TestElement.TEST_CLASS, HEADER_TEST_CLASS_NAME);
        // 设置GUI类名称
        headerManager.setProperty(TestElement.GUI_CLASS, HEADER_GUI_CLASS_NAME);
        // 设置启用
        headerManager.setEnabled(true);
        // 设置请求头名称
        headerManager.setName("请求头配置");
        // 设置请求头注释
        headerManager.setComment("");
        // 设置请求头
        JmeterOptional.ofNullable(this.requestHeaders).orElse(
                JmeterOptional.of(Collections.singletonMap("Content-Type", "application/x-www-form-urlencoded"))
        ).ifPresent(headers -> headers.forEach((key, value) -> headerManager.add(new Header(key, value))));
        // 返回请求头管理器
        return headerManager;
    }

    /**
     * 初始化参数
     *
     * @return 返回参数
     */
    private Arguments initArguments() {
        // 创建参数
        Arguments arguments = new Arguments();
        // 设置测试类名称
        arguments.setProperty(TestElement.TEST_CLASS, ARGUMENTS_TEST_CLASS_NAME);
        // 设置GUI类名称
        arguments.setProperty(TestElement.GUI_CLASS, ARGUMENTS_GUI_CLASS_NAME);
        // 设置启用
        arguments.setEnabled(true);
        // 如果请求参数不为空，则添加请求参数
        if (this.parameters != null) {
            // 如果为请求体参数，则使用请求体参数形式
            if (this.isRequestBodyParameter) {
                // 创建http请求参数
                HTTPArgument argument = new HTTPArgument("", this.parameters.values().iterator().next());
                // 设置不进行编码
                argument.setAlwaysEncoded(false);
                // 添加参数
                arguments.addArgument(argument);
            }
            // 否则使用表单形式
            else {
                // 获取请求参数集合
                Set<Map.Entry<String, String>> entrySet = this.parameters.entrySet();
                // 遍历请求参数集合
                for (Map.Entry<String, String> entry : entrySet) {
                    // 添加参数
                    arguments.addArgument(new HTTPArgument(entry.getKey(), entry.getValue()));
                }
            }
        }
        // 返回参数
        return arguments;
    }
}
